package cn.yangliu.tools;

import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import cn.yangliu.constant.Constants;
import cn.yangliu.entity.Dictionary;
import cn.yangliu.service.DictionaryService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import javafx.application.Platform;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * the type of serial-port-tool
 *
 * <p>
 * .
 *
 * @author 杨柳
 * @date 2021-10-11
 */
@Component
public class UpgradeHandler implements Runnable {

    /**
     * 日志记录操作对象 slf4j门面框架
     * 定义为常量
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(UpgradeHandler.class);

    @Value("${version}")
    private String version;

    @Autowired
    private DictionaryService dictionaryService;

    private volatile boolean downloaded = false;
    private volatile int limit;
    private volatile int interval;

    private volatile String urlPrefix;

    private volatile boolean notNotifyMore = false;

    public void hook() {
        List<Dictionary> updateConfig = dictionaryService.findByGroupName("update_config");
        Map<String, Dictionary> configMap = updateConfig.stream().collect(
                Collectors.toMap(Dictionary::getName, data -> data));
        limit = configMap.get("UPDATE_CHECK_LIMIT").intValue();
        interval = configMap.get("UPDATE_CHECK_INTERVAL").intValue();
        urlPrefix = configMap.get("UPDATE_URL_PREFIX").getVal();
    }

    @PostConstruct
    public void initialize() {
        LOGGER.info("当前版本: {}", version);
        Constants.POOL.execute(()->{
            try {
                TimeUnit.SECONDS.sleep(30);
            } catch (InterruptedException e) {
                LOGGER.error(e.getMessage(), e);
            }
            hook();
            Constants.POOL.execute(this);
        });
    }

    @Override
    public void run() {
        int index = 0;
        String[] split = version.split("\\.");

        int highVersion = Integer.parseInt(split[0]);
        int middleVersion = Integer.parseInt(split[1]);
        int smallVersion = Integer.parseInt(split[2]);
        // 检查更新失败次数达到上限后不再检查
        while (index < limit && !downloaded) {
            try {
                String url = urlPrefix + "/check/" + highVersion + "/" + middleVersion + "/" + smallVersion;

                String result = CommClient.sendGetRequest(url);

                if (StringUtils.isEmpty(result)) {
                    return;
                }

                JSONObject json = JSON.parseObject(result);

                String path = json.getString("path");

                String md5String = json.getString("md5");

                //下载url
                String downloadUrl = urlPrefix + "/download/" + path;

                //保存路径 临时文件
                String savePath = System.getProperty(
                        "user.dir") + File.separator + "download" + File.separator + path + ".tmp";

                if (FileUtil.exist(savePath)) {
                    FileUtil.del(savePath);
                }
                if (FileUtil.exist(
                        AppHandler.USER_DIR + File.separator + "download" + File.separator + "serial-port-tool-" + json.getString(
                                "version") + ".zip")) {
                    alter(json.getString("version"), json.getString("description"));
                    return;
                }
                //下载
                CommClient.clientSaveDownload(downloadUrl, savePath);

                // 校验md5
                Digester md5 = new Digester(DigestAlgorithm.MD5);
                String digestHex = md5.digestHex(FileUtil.file(savePath));
                if (!Objects.equals(md5String, digestHex)) {
                    return;
                }

                //将临时文件更名
                FileUtils.moveFile(FileUtil.file(savePath), FileUtil.file(
                        AppHandler.USER_DIR + File.separator + "download" + File.separator + "serial-port-tool-" + json.getString(
                                "version") + ".zip"));
                downloaded = true;
                if (notNotifyMore) {
                    return;
                }
                alter(json.getString("version"), json.getString("description"));
            } catch (IOException e) {
                LOGGER.error(e.getMessage(), e);
            } finally {
                index++;
                try {
                    TimeUnit.MINUTES.sleep(interval);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    LOGGER.error(e.getMessage(), e);
                }
            }

        }
    }

    private void alter(String version, String description) {
        Platform.runLater(() -> {
            try {
                ButtonType openDownloadDirBtn = new ButtonType("打开下载目录", ButtonBar.ButtonData.OK_DONE);
                Dialog<ButtonType> dialog = new Dialog<>();
                Stage stage = (Stage)dialog.getDialogPane().getScene().getWindow();

                dialog.setTitle("提示");
                dialog.setContentText("有新的版本更新" + version + ", 关闭程序后, 在下载目录解压出来替换原内容即可更新, 更新内容:\n" + description);

                dialog.getDialogPane().getButtonTypes().addAll(openDownloadDirBtn, ButtonType.CLOSE);

                stage.setAlwaysOnTop(true);
                // Add a custom icon.
                stage.getIcons().add(new Image(new ClassPathResource("imgs/64.png").getInputStream()));


                Optional<ButtonType> optional = dialog.showAndWait();
                if (optional.isPresent() && optional.get() ==  ButtonType.CANCEL) {
                    notNotifyMore = true;
                }
                if (optional.isPresent() && optional.get() == openDownloadDirBtn) {

                    java.awt.Desktop.getDesktop().open(
                            FileUtil.file(AppHandler.USER_DIR + File.separator + "download"));
                }
            } catch (IOException e) {
                LOGGER.error(e.getMessage(), e);
            }
        });
    }
}
